OpenGL几何着色器
几何着色器位于光栅化之前的 图元处理 的管线段内。
几何着色器允许同时访问图元中的所有顶点。(即逐图元处理)
允许的操作:
- 输出原图元保持不变。
- 输出修改了顶点位置的图元。
- 输出不同类型的图元。
- 输出更多的其他图元。
- 删除图元(不输出)。
相关函数和内置变量:
EmitVertex();
:完成了顶点属性相关计算并准备输出顶点。
EndPrimitive();
:完成了组成图元的一组顶点的定义。
gl_PrimitiveIDIn
:输入的图元ID。
修改图元
图元输出类型选项:
使用layout(...) in
指定;
points
- 每次调用顶点数量:1lines
- 每次调用顶点数量:2lines_adjacency
- 每次调用顶点数量:4triangles
- 每次调用顶点数量:3triangles_adjacency
- 每次调用顶点数量:6
示例一:膨胀效果
//基本原理:把顶点沿着其法线移动。
void main (void)
{
for (int i=0; i<3; i++)
{ gl_Position = proj_matrix *
(gl_in[i].gl_Position + normalize(vec4(varyingNormal[i],1.0))*0.4);
varyingNormalG = varyingNormal[i];
varyingLightDirG = varyingLightDir[i];
varyingHalfVectorG = varyingHalfVector[i];
EmitVertex();//完成了顶点属性相关计算并准备输出顶点。
}
EndPrimitive();//完成了组成图元的一组顶点的定义。
}
示例一:爆炸效果
//基本原理:把顶点沿着图元上顶点的法线平均值(即三角形面法向量)移动。
void main (void)
{ vec4 triangleNormal =
vec4(((varyingNormal[0]+varyingNormal[1]+varyingNormal[2])/3.0),1.0);
for (int i=0; i<3; i++)
{ gl_Position = proj_matrix *
(gl_in[i].gl_Position + normalize(triangleNormal)*0.4);
varyingNormalG = varyingNormal[i];
varyingLightDirG = varyingLightDir[i];
varyingHalfVectorG = varyingHalfVector[i];
EmitVertex();
}
EndPrimitive();
}
删除图元
不进行其他操作,直接调用EndPrimitive()
来实现删除图元。
示例:删除特定图元
void main (void)
{ if (mod(gl_PrimitiveIDIn,3)!=0)
{ for (int i=0; i<3; i++)
{ gl_Position = proj_matrix * gl_in[i].gl_Position;
varyingNormalG = varyingNormal[i];
varyingLightDirG = varyingLightDir[i];
varyingHalfVectorG = varyingHalfVector[i];
EmitVertex();
}
}
EndPrimitive();
}
添加图元
示例:在三角形图元中间插入点变成三个三角形图元:
vec3 newPoints[], lightDir[];
float sLen = 0.005;
void setOutputValues(int p, vec3 norm)
{ varyingNormal = norm;
varyingLightDir = lightDir[p];
varyingVertPos = newPoints[p];
gl_Position = proj_matrix * vec4(newPoints[p], 1.0);
}
void makeNewTriangle(int p1, int p2)
{ // generate vertex normal for this triangle
vec3 c1 = normalize(newPoints[p1] - newPoints[3]);
vec3 c2 = normalize(newPoints[p2] - newPoints[3]);
vec3 norm = cross(c1,c2);
// generate and emit the three vertices
setOutputValues(p1, norm); EmitVertex();
setOutputValues(p2, norm); EmitVertex();
setOutputValues(3, norm); EmitVertex();
EndPrimitive();
}
void main(void)
{ // offset the three triangle vertices by the surface normal
vec3 sp0 = gl_in[0].gl_Position.xyz + varyingOriginalNormal[0]*sLen;
vec3 sp1 = gl_in[1].gl_Position.xyz + varyingOriginalNormal[1]*sLen;
vec3 sp2 = gl_in[2].gl_Position.xyz + varyingOriginalNormal[2]*sLen;
// compute the new points comprising a small pyramid
newPoints[0] = gl_in[0].gl_Position.xyz;
newPoints[1] = gl_in[1].gl_Position.xyz;
newPoints[2] = gl_in[2].gl_Position.xyz;
newPoints[3] = (sp0 + sp1 + sp2)/3.0; // spike point
// compute the directions from the vertices to the light
lightDir[0] = light.position - newPoints[0];
lightDir[1] = light.position - newPoints[1];
lightDir[2] = light.position - newPoints[2];
lightDir[3] = light.position - newPoints[3];
// build three new triangles to form a small surface pyramid
makeNewTriangle(0,1); // the third point is always the spike point
makeNewTriangle(1,2);
makeNewTriangle(2,0);
}
修改图元类型
示例:三角形图元修改成放射状的线图元:
void main(void)
{ // offset the three triangle vertices by the surface normal
vec3 op0 = gl_in[0].gl_Position.xyz;
vec3 op1 = gl_in[1].gl_Position.xyz;
vec3 op2 = gl_in[2].gl_Position.xyz;
vec3 ep0 = gl_in[0].gl_Position.xyz + varyingNormal[0]*sLen;
vec3 ep1 = gl_in[1].gl_Position.xyz + varyingNormal[1]*sLen;
vec3 ep2 = gl_in[2].gl_Position.xyz + varyingNormal[2]*sLen;
// compute the new points comprising a small line segment
vec3 newPoint1 = (op0 + op1 + op2)/3.0; // start point
vec3 newPoint2 = (ep0 + ep1 + ep2)/3.0; // end point
gl_Position = proj_matrix * vec4(newPoint1, 1.0);
varyingVertPosG = newPoint1;
varyingLightDirG = light.position - newPoint1;
varyingNormalG = varyingNormal[0];
EmitVertex();
gl_Position = proj_matrix * vec4(newPoint2, 1.0);
varyingVertPosG = newPoint2;
varyingLightDirG = light.position - newPoint2;
varyingNormalG = varyingNormal[1];
EmitVertex();
EndPrimitive();
}